#pragma once

#include <iostream>
#include <string>
#include "../comm/Log.hpp"
#include "../comm/util.hpp"

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>


namespace ns_runner
{
    using namespace ns_util;

    class Runner
    {
    public:
        static void SourceLimit(int cpu_limit, int mem_limit)
        {
            struct rlimit cpu;
            cpu.rlim_cur = cpu_limit;
            cpu.rlim_max = RLIM_INFINITY;

            struct rlimit mem;
            mem.rlim_cur = mem_limit;
            mem.rlim_max = RLIM_INFINITY;

            setrlimit(RLIMIT_CPU, &cpu);
            setrlimit(RLIMIT_AS, &mem);
        }

        static int Run(const std::string& file_name, int cpu_limit, int mem_limit)
        {
            std::string _execute = PathUtil::Exe(file_name);
            std::string _stdin = PathUtil::Stdin(file_name);
            std::string _stdout = PathUtil::Stdout(file_name);
            std::string _stderr = PathUtil::Stderr(file_name);
            
            umask(0);
            int _in_fd = open(_stdin.c_str(), O_CREAT | O_RDONLY, 0644);
            int _out_fd = open(_stdout.c_str(), O_CREAT | O_WRONLY, 0644);
            int _err_fd = open(_stderr.c_str(), O_CREAT | O_WRONLY, 0644);
            if(_in_fd < 0 || _out_fd < 0 || _err_fd < 0)
            {
                LOG(FATAL, "运行时打开文件失败\n");
                return -1;
            }

            pid_t id = fork();
            if(id < 0)
            {
                close(_in_fd);
                close(_out_fd);
                close(_err_fd);

                LOG(FATAL, "运行时创建子进程失败\n");
                return -2;
            }
            else if(id == 0)
            {
                dup2(_in_fd, 0);
                dup2(_out_fd, 1);
                dup2(_err_fd, 2);
                // LOG(INFO, "资源限制\n");
                SourceLimit(cpu_limit, mem_limit);
                // LOG(INFO, "程序替换 execl 执行: %s\n", _execute.c_str());
                execl(_execute.c_str(), _execute.c_str(), nullptr);
                exit(1);
            }
            else  
            {
                close(_in_fd);
                close(_out_fd);
                close(_err_fd);
                int status = 0;
                waitpid(id, &status, 0);
                LOG(INFO, "运行结束，返回值：%d\n", status & 0x7f); 

                return status & 0x7f;
            }
        }
    };
}